package com.xinqi.common.base.current;

import com.google.common.collect.ClassToInstanceMap;
import com.google.common.collect.MutableClassToInstanceMap;
import com.xinqi.common.base.user.Account;
import org.springframework.core.NamedThreadLocal;

import javax.annotation.Nonnull;
import java.util.Map;
import java.util.Optional;

/**
 * 当前上下文信息
 *
 * @author linkai
 * @since 2023/01/13
 */
public interface Currents {

    @SuppressWarnings("unchecked")
    @Nonnull
    static <T> T require(Class<T> type) {
        T t = (T) Holder.getCurrentMap().get(type);
        if (t == null) {
            throw new IllegalArgumentException("current class instance not exist");
        }
        return t;
    }

    @SuppressWarnings("unchecked")
    static <T> T get(Class<T> type) {
        return (T) Holder.getCurrentMap().get(type);
    }

    /**
     * 获取当前用户ID
     */
    static Long userId() {
        return Optional.ofNullable(get(Account.class))
            .map(Account::getId).orElse(null);
    }

    /**
     * 获取当前用户名称
     */
    @SuppressWarnings("unchecked")
    static <T> T userName() {
        return (T) Optional.ofNullable(get(Account.class))
            .map(Account::getUsername).orElse(null);
    }

    @SuppressWarnings("unchecked")
    static <T> Optional<T> resolve(Class<T> type) {
        return Optional.ofNullable((T) Holder.getCurrentMap().get(type));
    }

    @SuppressWarnings("unchecked")
    static void put(Object o) {
        put((Class<? super Object>) Holder.getClass(o), o);
    }

    static <T> void put(Class<? super T> type, T o) {
        Holder.getCurrentMap().put(type, o);
    }

    static <T> void remove(Class<? super T> type) {
        Holder.getCurrentMap().remove(type);
    }

    static void remove() {
        Optional.ofNullable(Holder.getThreadLocal().get()).ifPresent(Map::clear);
        Holder.getThreadLocal().remove();
    }

    static void getReq() {

    }

    final class Holder {
        private static final ThreadLocal<ClassToInstanceMap<Object>> CURRENT_THREAD_LOCAL = new NamedThreadLocal<>("Current-Context");

        private static ClassToInstanceMap<Object> getCurrentMap() {
            ClassToInstanceMap<Object> map = CURRENT_THREAD_LOCAL.get();
            if (map == null) {
                map = MutableClassToInstanceMap.create();
                CURRENT_THREAD_LOCAL.set(map);
            }
            return map;
        }

        private static ThreadLocal<ClassToInstanceMap<Object>> getThreadLocal() {
            return CURRENT_THREAD_LOCAL;
        }

        private static Class<?> getClass(Object o) {
            return o.getClass();
        }
    }
}
